import string
csharp_data_type_decode = {
    "byte":     "ToChar",
    "ushort":   "ToUInt16",
    "short":    "ToInt16",
    "int":      "ToInt32",
    "float":    "ToSingle",
    "ulong":    "ToUInt64",
    "uint":     "ToUInt32",    
    #"string":   "",
    #"tstring":  "",
}

need_translate_endian_type_dict = {
                                   "ushort":"(Int16)",
                                   "short":"(Int16)",
                                   "int":"(Int32)", 
                                   "uint":"(Int32)", 
                                   "ulong":"(Int64)",
                                   }

xml_csharp_data_map = {#typeinxml :typeinC#
                       "byte":  "byte",
                       "word":  "ushort",
                       "short": "short",
                       "int":   "int",
                       "dword": "uint",
                       "float": "float",
                       "uint64":"ulong",
                       "string":"string",
                       "tstring":"string",
                       }
csharp_data_type = [xml_csharp_data_map[m] for m in xml_csharp_data_map.keys()]

protocols = {}
"""             "CAlphaStruct":
             {
              "namecount":    ["int",     None,    None],
              "name":         ["string",  "100",   None],
              "passwordcount":["int",     None,    None],
              "password":     ["string",  "100",   None],
             },
             "CBetaStruct":
             {
              "count":        ["int",     None,    None],
              "rolename":     ["string",  "100",   None],
              "level":        ["int",     None,    None],
              "hair":         ["char",    None,    None],
              "petcount":     ["int",     None,    None],
              "pet":          ["int",     "2",     "petcount"],
             },
             "CCharry":
             {
              "logintimeout":     ["int",            None,    None],
              "logintime":        ["int",            "2",     "logintimeout"],
              "login":            ["CAlphaStruct",   None,    None],
              "accountnamecount": ["int",            None,    None],
              "accountname":      ["string",         "100",   None],
              "rolecount":        ["int",            None,    None],
              "role":             ["CBetaStruct",    "3",     "rolecount"],
             },
}
"""
                
define_map = {#definename: value
              }


def fun_getMemberStatement(membername, memberConfigure):
    global define_map
    name = membername
    datatype = memberConfigure[0]
    count = memberConfigure[1]
    refer = memberConfigure[2]
    initialize = ""
    if count <> None and datatype <> "string":
        initialize += " = new %(datatype)s[%(count)s]"%(locals())
        if datatype in protocols.keys():
            print "the datatype ", datatype, " need "
            real_count = count
            if real_count in define_map.keys():
                real_count = define_map[real_count]
            c = int(real_count)
            initialize += "{"+string.join(["new %s()"%datatype for i in range(c)], ",")+"}";
            print "the datatype ", datatype, " need ",initialize
        datatype += "[]"
    elif count == None and datatype not in csharp_data_type:
        initialize += " = new %(datatype)s()"%(locals())
    elif datatype == "string":
        initialize += """ = "" """
    return """    public %(datatype)s %(name)s %(initialize)s;
""" %(locals())

def fun_getStatements(memberDict):
    ret = ""
    for m in memberDict.keys():
        ret += fun_getMemberStatement(m, memberDict[m])
    return ret

def fun_getStatements_l(memberList):
    ret = ""
    for m in memberList:
        ret += fun_getMemberStatement(m[3], m)
    return ret


def fun_getMemberEncode(membername, memberConfigure):
    global protocols
    name = membername
    datatype = memberConfigure[0]
    count = memberConfigure[1]
    refer = memberConfigure[2]
    ret = ""
    if count == None:
        if datatype not in csharp_data_type:
            if datatype not in protocols.keys():
                print "missing the datatype ",datatype 
                raise TypeError
            ret = """
            int _%(name)s_temp_capacity = 0;
            byte[] _%(name)s_temp = %(name)s.GetSendbuf(ref _%(name)s_temp_capacity);
            if(_%(name)s_temp == null)
            {
                return null;
            }
            Array.Copy(_%(name)s_temp, 0, c, capacity, _%(name)s_temp_capacity);
            capacity += _%(name)s_temp_capacity;
"""%(locals())
        elif datatype == "string":
            print"string without length limit"
            raise TypeError
        else:
            needtranslate = name
            if datatype in need_translate_endian_type_dict.keys():
                recast = need_translate_endian_type_dict[datatype]
                needtranslate="""IPAddress.HostToNetworkOrder(%(recast)s%(name)s)"""%(locals())
            if datatype == "float":
                ret = """
            byte[] __temp%(name)s=BitConverter.GetBytes(%(name)s);
            Array.Reverse(__temp%(name)s, 0, 4);
            Array.Copy(__temp%(name)s, 0, c, capacity, sizeof(%(datatype)s));
            capacity += sizeof(%(datatype)s);
"""%(locals())
            else:    
                ret = """
            Array.Copy(BitConverter.GetBytes(%(needtranslate)s), 0, c, capacity, sizeof(%(datatype)s));
            capacity += sizeof(%(datatype)s);
"""%(locals())
            
    else:#count not None
        if datatype == "string":
            ret = """
            //if (%(count)s > %(name)s.Length)
            //{
            //    return null;
            //}
            short _%(name)s_length =(short)(%(name)s.Length);
            Array.Copy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(_%(name)s_length)), 0, c, capacity, sizeof(short));
            capacity += sizeof(short);
            Array.Copy(Encoding.ASCII.GetBytes(%(name)s), 0, c, capacity, _%(name)s_length);
            capacity += _%(name)s_length;
"""%(locals())
        else:#not the string
            if datatype not in csharp_data_type:
                if datatype not in protocols.keys():
                    raise TypeError
                subEncode = """
                int _%(name)s_temp_capacity = 0;
                byte[] _%(name)s_temp = %(name)s[i].GetSendbuf(ref _%(name)s_temp_capacity);
                if(_%(name)s_temp == null)
                {
                    return null;
                }
                Array.Copy(_%(name)s_temp, 0, c, capacity, _%(name)s_temp_capacity);
                capacity += _%(name)s_temp_capacity;
"""%(locals())
            else: #datatype is csharp type
                needtranslate = "%(name)s[i]"%(locals())
                if datatype in need_translate_endian_type_dict.keys():
                    recast = need_translate_endian_type_dict[datatype]
                    needtranslate="""IPAddress.HostToNetworkOrder(%(recast)s%(name)s[i])"""%(locals())
                if datatype == "float":
                    subEncode = """
                byte[] __temp%(name)s=BitConverter.GetBytes(%(name)s[i]);
                Array.Reverse(__temp%(name)s, 0, 4);
                Array.Copy(__temp%(name)s, 0, c, capacity, sizeof(%(datatype)s));
                capacity += sizeof(%(datatype)s);
"""%(locals())                    
                else:
                    subEncode = """
                Array.Copy(BitConverter.GetBytes(%(needtranslate)s), 0, c, capacity, sizeof(%(datatype)s));
                capacity += sizeof(%(datatype)s);
"""%(locals())
            checkrefer = ""
            if refer <> None:
                checkrefer = """
            if(%(refer)s > %(count)s)
            {
                return null;
            }
"""%(locals())
                count = refer
            ret = """
            %(checkrefer)s
            if(%(count)s > %(name)s.Length)
            {
                return null;
            }
            for (int i=0; i<%(count)s; ++i)
            {
                %(subEncode)s
            }
"""%(locals())
    return ret

def fun_getEncodeFunction(memberDict):
    encodes = ""
    for m in memberDict.keys():
        encodes += fun_getMemberEncode(m, memberDict[m])
    ret = """
    public override byte[] GetSendbuf(ref int capacity)
    {
        try
        {
            byte[] c = new byte[1024];
            capacity = 0;
            %(encodes)s
            return c;
        }
        catch (System.Exception ex)
        {
            ex.GetType();
        }
        return null;
    }

"""%(locals())
    return ret

def fun_getEncodeFunction_l(memberList):
    encodes = ""
    for m in memberList:
        encodes += fun_getMemberEncode(m[3], m)
    ret = """
    public override byte[] GetSendbuf(ref int capacity)
    {
        try
        {
            byte[] c = new byte[1024];
            capacity = 0;
            %(encodes)s
            return c;
        }
        catch (System.Exception ex)
        {
            ex.GetType();
        }
        return null;
    }

"""%(locals())
    return ret

        
def fun_getMemberDecode(membername, memberConfigure):
    name = membername
    datatype = memberConfigure[0]
    count = memberConfigure[1]
    refer = memberConfigure[2]
    ret = ""
    if count == None:
        if datatype not in csharp_data_type:
            if datatype not in protocols.keys():
                raise TypeError
            ret = """
            %(datatype)s _%(name)s = new %(datatype)s();
            int _%(name)s_indent = _%(name)s.Parse(recvBuf, indent);
            if (_%(name)s_indent == -1)
            {
                return -1;
            }
            %(name)s = _%(name)s;
            indent = _%(name)s_indent;
"""%(locals())
        elif datatype == "string":
            print"string without length limit"
            raise TypeError
        else:
            decodefun = csharp_data_type_decode.get(datatype, None)
            if decodefun == None:
                print "missing decodefun ", datatype
                raise TypeError
            char_transfer = ""
            if datatype == "byte":
                char_transfer = "(byte)"
            translate_endian = ""
            if datatype in need_translate_endian_type_dict.keys():
                recast = need_translate_endian_type_dict[datatype]
                translate_endian = """
            %(name)s = (%(datatype)s)IPAddress.NetworkToHostOrder(%(recast)s%(name)s);
"""%(locals())
            if datatype == "float":
                ret = """
            Array.Reverse(recvBuf,indent,4);
            %(name)s = BitConverter.%(decodefun)s(recvBuf, indent);
            indent += sizeof(%(datatype)s);
"""%(locals())                
            else:
                ret = """
            %(name)s = %(char_transfer)sBitConverter.%(decodefun)s(recvBuf, indent);
            %(translate_endian)sindent += sizeof(%(datatype)s);
"""%(locals())
    else:#count not None
        if datatype == "string":
            ret = """
            short _%(name)s_length = BitConverter.ToInt16(recvBuf, indent);
            _%(name)s_length = (short)IPAddress.NetworkToHostOrder((Int16)_%(name)s_length);
            indent += sizeof(short);
            //if (_%(name)s_length>%(count)s)
            //{
            //    return -1;
            //}
            %(name)s = Encoding.ASCII.GetString(recvBuf, indent, _%(name)s_length);
            indent += _%(name)s_length;
"""%(locals())
        else:#not the string
            if datatype not in csharp_data_type:
                if datatype not in protocols.keys():
                    raise TypeError
                subEncode = """
                %(datatype)s _%(name)s = new %(datatype)s();
                int _%(name)s_indent = _%(name)s.Parse(recvBuf, indent);
                if (_%(name)s_indent == -1)
                {
                    return -1;
                }
                %(name)s[i] = _%(name)s;
                indent = _%(name)s_indent;
"""%(locals())
            else: #datatype is csharp type
                decodefun = csharp_data_type_decode.get(datatype, None)
                if decodefun == None:
                    raise TypeError
                char_transfer = ""
                if datatype == "byte":
                    char_transfer = "(byte)"
                translate_endian = ""
                if datatype in need_translate_endian_type_dict.keys():
                    recast = need_translate_endian_type_dict[datatype]
                    translate_endian = """
                %(name)s[i] = (%(datatype)s)IPAddress.NetworkToHostOrder(%(recast)s%(name)s[i]);
"""%(locals())
                if datatype == "float":
                    subEncode = """
                Array.Reverse(recvBuf,indent,4);
                %(name)s[i] = sBitConverter.%(decodefun)s(recvBuf, indent);
                indent += sizeof(%(datatype)s);
"""%(locals())                    
                else:
                    subEncode = """
                %(name)s[i] = %(char_transfer)sBitConverter.%(decodefun)s(recvBuf, indent);
                %(translate_endian)sindent += sizeof(%(datatype)s);
"""%(locals())
            checkrefer = ""
            if refer <> None:
                checkrefer = """
            if(%(refer)s > %(count)s)
            {
                return -1;
            }
"""%(locals())
                count = refer
            ret = """
            %(checkrefer)s
            //if(%(name)s.Length > %(count)s)
            //{
            //    return -1;
            //}
            for (int i=0; i<%(count)s; ++i)
            {
                %(subEncode)s
            }
"""%(locals())
    return ret

def fun_getDecodeFunction(memberDict):
    decodes = ""
    for m in memberDict.keys():
        decodes += fun_getMemberDecode(m, memberDict[m])
    ret = """
    public int Parse(byte[] recvBuf, int indent)
    {
        try
        {
            %(decodes)s
            return indent;
        }
        catch (System.Exception ex)
        {
            ex.GetType();
        }
        return -1;
    }
"""%(locals())
    return ret

def fun_getDecodeFunction_l(memberList):
    decodes = ""
    for m in memberList:
        decodes += fun_getMemberDecode(m[3], m)
    ret = """
    public int Parse(byte[] recvBuf, int indent)
    {
        try
        {
            %(decodes)s
            return indent;
        }
        catch (System.Exception ex)
        {
            ex.GetType();
        }
        return -1;
    }
"""%(locals())
    return ret


def fun_getDebugPrint(membername, memberConfigure):
    name = membername
    datatype = memberConfigure[0]
    count = memberConfigure[1]
    refer = memberConfigure[2]
    if count == None:
        if datatype in csharp_data_type:
            return """UnityEngine.Debug.Log("%(name)s: " + %(name)s);"""%(locals())
        else:
            return """UnityEngine.Debug.Log("%(name)s: ") ;%(name)s.DebugPrint();"""%(locals())
    else:
        if datatype == "string":
            return """UnityEngine.Debug.Log("%(name)s: " + %(name)s);"""%(locals())
        elif datatype in csharp_data_type:
            return """foreach(%(datatype)s o in %(name)s){UnityEngine.Debug.Log("%(name)s[]: " + o);}"""%(locals())
        else:
            return """foreach(%(datatype)s o in %(name)s){UnityEngine.Debug.Log("%(name)s[]: ") ;o.DebugPrint();}"""%(locals())
            
def fun_getDebugFunction(memberDict):
    prints = ""
    for m in memberDict.keys():
        prints += fun_getDebugPrint(m, memberDict[m])+"\n        "
    str = """
    public void DebugPrint()
    {
        %(prints)s
    }
"""%(locals())
    return str

def fun_getDebugFunction_l(memberList):
    prints = ""
    for m in memberList:
        prints += fun_getDebugPrint(m[3], m)+"\n        "
    str = """
    public void DebugPrint()
    {
        %(prints)s
    }
"""%(locals())
    return str


def fun_getCSharpClass(classname, memberDict):
    statement = fun_getStatements(memberDict)
    encodes = fun_getEncodeFunction(memberDict)
    decodes = fun_getDecodeFunction(memberDict)
    debugprint = fun_getDebugFunction(memberDict)
    ret = """
public class %(classname)s :PROTOCOLDEFINE
{
%(statement)s
    %(encodes)s
    %(decodes)s
    %(debugprint)s
}
"""%(locals())
    return ret

def fun_getCSharpClass_l(classname, memberList):
    statement = fun_getStatements_l(memberList)
    encodes = fun_getEncodeFunction_l(memberList)
    decodes = fun_getDecodeFunction_l(memberList)
    debugprint = fun_getDebugFunction_l(memberList)
    ret = """
public class %(classname)s :PROTOCOLDEFINE
{
%(statement)s
    %(encodes)s
    %(decodes)s
    %(debugprint)s
}
"""%(locals())
    return ret



def fun_getDefine(define):
    global define_map
    define_map[define[0]] = define[1]
    #return "#define %s %s"%(define[0], define[1])
    return "    public const int %s = %s;"%(define[0], define[1])

def fun_getAllDefines(definelist):
    return """
public class PROTOCOLDEFINE{
%s
public virtual byte[] GetSendbuf(ref int capacity){return null;}
}
"""%(string.join(map(fun_getDefine, definelist), "\n"))

def fun_getMsgID(mesageid):
    return "    public static int %s = %s;"%(mesageid[0], mesageid[1])

def fun_getALLMsgIDs(messagelist):
    return """
public class MSGID{
%s
}
"""%(string.join(map(fun_getMsgID, messagelist), "\n"))

def fun_getCase(messageid):
    msg = messageid[0]
    id = messageid[1]
    type = messageid[2]
    return """
            case %(id)s:
                {
                    %(type)s _%(type)s = new %(type)s();
                    int __%(type)s_indent = _%(type)s.Parse(recvbuf, indent);
                    if (-1 != __%(type)s_indent)
                    {
                        handle_%(msg)s(_%(type)s, peer);
                        indent = __%(type)s_indent;
                    }
                }
                break;"""%(locals())

def fun_getHandleFunction(messagelist):
    return """
    public int Handle(byte[] recvbuf, int size, CSdSocket_TCP peer)
    {
        int indent = 0;
        try
        {
            if (size < 6)
            {
                return 0;
            }
            CSHARP_MSGHEADER head = new CSHARP_MSGHEADER();
            indent = head.Parse(recvbuf, indent);
            if (indent == -1)
            {
                UnityEngine.Debug.Log("parse head failed");
                return -1;
            }
            if (head.msgLength+6 > size)
            {
                return 0;
            }
            switch ((int)head.msgID)
            {
            %s
            default:
                {
                    UnityEngine.Debug.Log("unknown msgid :" + head.msgID);
                }
                break;
            }
        }
        catch (System.Exception )
        {
            
        }
        return indent;
    }
"""%(string.join(map(fun_getCase, messagelist), "\n"))


def fun_getHandleFunction_quick(messagelist):
    return """
    public int Handle(byte[] recvbuf, int size, CSdSocket_TCP peer)
    {
        int indent = 0;
        try
        {
            if (size < 6)
            {
                return 0;
            }
            CSHARP_MSGHEADER head = new CSHARP_MSGHEADER();
            indent = head.Parse(recvbuf, indent);
            if (indent == -1)
            {
                UnityEngine.Debug.Log("parse head failed");
                return -1;
            }
            if (head.msgLength+6 > size)
            {
                return 0;
            }
            QuickHandle q_h;
            if (quickHandle.TryGetValue((int)head.msgID, out q_h))
            {
                try
                {
                    indent = q_h(recvbuf, indent, peer);
                }
                catch (System.Exception ex)
                {
                    indent = (int)(head.msgLength + 6);
                    Debug.LogError("handle the msg :"+head.msgID + " failed " +ex.Message);
                }
            }
            else
            {
                //could not found the handler
                //todo take care of this
                indent = (int)(head.msgLength + 6);
            }
        }
        catch (System.Exception )
        {
            
        }
        return indent;
    }
"""#%(string.join(map(fun_getCase, messagelist), "\n"))


def getProtocolHandleFunction(messageid):
    type = messageid[2]
    msgname = messageid[0]
    return "    public virtual void handle_%(msgname)s(%(type)s p, CSdSocket_TCP peer){}"%(locals())

def getALLProtocolHandle(messagelist):
    return """
%s
"""%(string.join(map(getProtocolHandleFunction, messagelist), "\n"))


def fun_getInitDictionaryByMsgid(messageid):
    msg = messageid[0]
    id = messageid[1]
    type = messageid[2]
    return """        quickHandle.Add(%(id)s, _QuickHandle_%(type)s);"""%(locals())

def fun_getInitQuickDictionary(messagelist):
    return """
    public void init()
    {
        quickHandle.Clear();
%s
    }
"""%(string.join(map(fun_getInitDictionaryByMsgid, messagelist), "\n"))

g_CheckList = list()
def fun_getQuickHandle(messageid):
    global g_CheckList
    msg = messageid[0]
    id = messageid[1]
    type = messageid[2]
    if type in g_CheckList:
        return""""""
    g_CheckList.append(type)
    return """
    public int _QuickHandle_%(type)s(byte[] recvbuf, int indent, CSdSocket_TCP peer)
    {
        %(type)s _%(type)s = new %(type)s();
        int __%(type)s_indent = _%(type)s.Parse(recvbuf, indent);
        if (-1 != __%(type)s_indent)
        {
            handle_%(msg)s(_%(type)s, peer);
        }
        return __%(type)s_indent;
    }
"""%(locals())


def fun_getALLProtocolQuickHandle(messagelist):
    global g_CheckList
    g_CheckList = []
    return """
%s
"""%(string.join(map(fun_getQuickHandle, messagelist), "\n"))

def getProtocolMgrClass(messagelist):
    handle = fun_getHandleFunction(messagelist)
    handlefunctions = getALLProtocolHandle(messagelist)
    return """
public class CProtocolMgr:MSGID
{
%(handle)s
%(handlefunctions)s
}
"""%(locals())

def getProtocolMgrClass_quick_find(messagelist):
    quickhandlefunctions = fun_getALLProtocolQuickHandle(messagelist)
    quickhandle = fun_getHandleFunction_quick(messagelist)
    handlefunctions = getALLProtocolHandle(messagelist)
    initquickdictionary = fun_getInitQuickDictionary(messagelist)
    return """
public class CProtocolMgr:MSGID
{
    public delegate int QuickHandle(byte[] recvbuf, int indent, CSdSocket_TCP peer);

    public Dictionary<int, QuickHandle> quickHandle = new Dictionary<int, QuickHandle>();
%(initquickdictionary)s
%(quickhandlefunctions)s
%(quickhandle)s
%(handlefunctions)s
}
"""%(locals())


def fun_build_protocol(protocols):
    ret = ""
    for classname in protocols.keys():
        #ret += fun_getCSharpClass(classname, protocols[classname][0])
        ret += fun_getCSharpClass_l(classname, protocols[classname][1])
    return ret

def fun_build_file(full_path_name, protocols, definelist, msgidlist):
    w = open(full_path_name, "w")
    #protoclmgr = getProtocolMgrClass(msgidlist)
    protoclmgr = getProtocolMgrClass_quick_find(msgidlist)
    msgids = fun_getALLMsgIDs(msgidlist)
    defines = fun_getAllDefines(definelist)
    protocolstr = fun_build_protocol(protocols)
    str = """using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Net;
%(msgids)s
%(defines)s
%(protocolstr)s
%(protoclmgr)s
"""%(locals())
    #print str
    w.writelines(str)
    w.close()
    
#-*- coding: GBK -*-

import sys
sys.path.append('/autotest_lib')
from parseProtocolXmlFile_lib import *

xml_path = "E:/mland2/common/protocol/xml/"
csharp_path = "E:/unitymland2/client/Assets/Resources/Scripts/GameServer/"


def fun_translate_struct():
    #"""
    
    fun_parseProtocolXml_UTF8_filename(xml_path + "gt-common.xml")
    fun_parseProtocolXml_UTF8_filename(xml_path + "commongamedata.xml")
    fun_parseProtocolXml_UTF8_filename(xml_path + "commondataproto.xml")
    fun_parseProtocolXml_UTF8_filename(xml_path + "bs-cli.xml")
    fun_parseProtocolXml_UTF8_filename(xml_path + "cli-common.xml")
    fun_parseProtocolXml_UTF8_filename(xml_path + "db-cli.xml")
    #"""
    fun_parseProtocolXml_UTF8_filename(xml_path + "gs-cli.xml")
    #"""
    fun_parseProtocolXml_UTF8_filename(xml_path + "ls-cli.xml")
    fun_parseProtocolXml_UTF8_filename(xml_path + "protocli.xml")
    fun_parseProtocolXml_UTF8_filename(xml_path + "ipad_specialization.xml")
    fun_parseProtocolXml_UTF8_filename(xml_path + "p2p.xml")
    #"""
    p = {}
    for struct in typelist:
        sname = struct[0]
        smembers = struct[1]
        s = {}
        memberlist = []
        for m in smembers:
            mname = m["name"]
            mtype = m["type"]
            mcount = m["count"]
            mrefer = m["refer"]
            if mtype in xml_csharp_data_map:mtype = xml_csharp_data_map[mtype]
            if mcount == "":mcount = None
            if mrefer == "":mrefer = None
            s[mname] = [mtype, mcount, mrefer]
            memberlist.append([mtype, mcount, mrefer, mname])
        p[sname] = [s, memberlist]
    return p

def fun_parse_build(targetfilename):
    global protocols
    protocols = fun_translate_struct()
    fun_build_file(targetfilename, protocols, defineList, messageListList)

fun_parse_build(csharp_path + "protocols.cs")
#fun_parse_build("D:/test-unity/Assets/protocols.cs")



